﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
using System.Windows.Media;

namespace CPPEI.Coolzon.UI
{
    [XPartIcon("pack://application:,,,/CPPEI.Coolzon.UI;component/Images/PartIcons/区域.png")]
    [System.ComponentModel.DisplayName("区域")]
    public partial class AreaPart : Part, IPartContainer
    {
        public Metadata.Area SceneMatadata
        {
            get
            {
                return (Metadata.Area)Metadata;
            }
            set
            {
                Metadata = value;
            }
        }

        /// <summary>
        /// 场景中包含的子组件集合
        /// </summary>
        PartCollection _partCollection;
        private Canvas _canvas;

        /// <summary>
        /// 标题描述属性
        /// </summary>
        [Category("Appearance")]
        [DisplayName("标题")]
        [Description("Header")]
        [Editor(typeof(XProperty.Editors.XStringEditor), typeof(XProperty.XPropertyValueEditor))]
        [XProperty(XPropertyCategory.Appearance, "Header", "标题")]
        public string XHeader
        {
            get
            {
                return (string)GetValue(XHeaderProperty);
            }
            set
            {
                SetValue(XHeaderProperty, value);
            }
        }

        /// <summary>
        /// 是否显示查询面板
        /// </summary>
        public bool IsShowQueryPanel
        {
            get
            {
                return (bool)GetValue(IsShowQueryPanelProperty);
            }
            set
            {
                SetValue(IsShowQueryPanelProperty, value);
            }
        }

        #region dependency property
        private static void propertyChangedCallback(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            AreaPart areaPart = d as AreaPart;
            areaPart.RaisePropertyChanged(e.Property.Name);
            areaPart.Refresh();
        }

        public static DependencyProperty XHeaderProperty = DependencyProperty.Register("XHeader",
            typeof(string),
            typeof(AreaPart),
            new PropertyMetadata(propertyChangedCallback));

        public static DependencyProperty XIsShowHeaderProperty = DependencyProperty.Register("XIsShowHeader",
            typeof(bool),
            typeof(AreaPart),
            new PropertyMetadata(propertyChangedCallback));

        public static DependencyProperty IsShowQueryPanelProperty = DependencyProperty.Register("IsShowQueryPanel",
            typeof(bool),
            typeof(AreaPart),
            new PropertyMetadata(propertyChangedCallback));

        public static DependencyProperty XHeaderBackgroundImageProperty = DependencyProperty.Register("XHeaderBackgroundImage",
            typeof(ImageSourceFile),
            typeof(AreaPart),
            new PropertyMetadata(propertyChangedCallback));
        public static DependencyProperty XHeaderHeightProperty = DependencyProperty.Register("XHeaderHeight",
            typeof(double),
            typeof(AreaPart),
            new PropertyMetadata(propertyChangedCallback));
        public static DependencyProperty XHeaderWidthProperty = DependencyProperty.Register("XHeaderWidth",
            typeof(double),
            typeof(AreaPart),
            new PropertyMetadata(propertyChangedCallback));
        public static DependencyProperty XHeaderPaddingProperty = DependencyProperty.Register("XHeaderPadding",
            typeof(Thickness),
            typeof(AreaPart),
            new PropertyMetadata(propertyChangedCallback));
        public static DependencyProperty XHeaderForegroundProperty = DependencyProperty.Register("XHeaderForeground",
            typeof(BrushEx),
            typeof(AreaPart),
            new PropertyMetadata(propertyChangedCallback));
        public static DependencyProperty XHeaderFontSizeProperty = DependencyProperty.Register("XHeaderFontSize",
            typeof(double),
            typeof(AreaPart),
            new PropertyMetadata(propertyChangedCallback));
        public static DependencyProperty XHeaderFontProperty = DependencyProperty.Register("XHeaderFont",
            typeof(FontFamily),
            typeof(AreaPart),
            new PropertyMetadata(propertyChangedCallback));
        #endregion

        #region XProperties

        [Category("Appearance")]
        [DisplayName("是否显示标题")]
        [Description("IsShowHeader")]
        [Editor(typeof(XProperty.Editors.XBooleanEditor), typeof(XProperty.XPropertyValueEditor))]
        [XProperty(XPropertyCategory.Appearance, "IsShowHeader", "是否显示标题")]
        public bool XIsShowHeader
        {
            get
            {
                return (bool)GetValue(XIsShowHeaderProperty);
            }
            set
            {
                SetValue(XIsShowHeaderProperty, value);
            }
        }

        [Category("Appearance")]
        [DisplayName("标题背景图片")]
        [Description("HeaderBackgroundImage")]
        [Editor(typeof(XProperty.Editors.XImageSourceFileEditor), typeof(XProperty.XPropertyValueEditor))]
        [XProperty(XPropertyCategory.Appearance, "HeaderBackgroundImage", "标题背景图片")]
        public ImageSourceFile XHeaderBackgroundImage
        {
            get
            {
                return (ImageSourceFile)GetValue(XHeaderBackgroundImageProperty);
            }
            set
            {
                SetValue(XHeaderBackgroundImageProperty, value);
            }
        }

        [Category("Appearance")]
        [DisplayName("标题高度")]
        [Description("HeaderHeight")]
        [Editor(typeof(XProperty.Editors.XDoubleEditor), typeof(XProperty.XPropertyValueEditor))]
        [XProperty(XPropertyCategory.Appearance, "HeaderHeight", "标题高度")]
        public double XHeaderHeight
        {
            get
            {
                return (double)GetValue(XHeaderHeightProperty);
            }
            set
            {
                SetValue(XHeaderHeightProperty, value);
            }
        }

        [Category("Appearance")]
        [DisplayName("标题宽度")]
        [Description("HeaderWidth")]
        [Editor(typeof(XProperty.Editors.XDoubleEditor), typeof(XProperty.XPropertyValueEditor))]
        [XProperty(XPropertyCategory.Appearance, "HeaderWidth", "标题宽度")]
        public double XHeaderWidth
        {
            get
            {
                return (double)GetValue(XHeaderWidthProperty);
            }
            set
            {
                SetValue(XHeaderWidthProperty, value);
            }
        }

        [Category("Appearance")]
        [DisplayName("标题内边距")]
        [Description("HeaderPadding")]
        [Editor(typeof(XProperty.Editors.XThicknessEditor), typeof(XProperty.XPropertyValueEditor))]
        [XProperty(XPropertyCategory.Appearance, "HeaderPadding", "标题内边距")]
        public Thickness XHeaderPadding
        {
            get
            {
                return (Thickness)GetValue(XHeaderPaddingProperty);
            }
            set
            {
                SetValue(XHeaderPaddingProperty, value);
            }
        }

        [Category("Appearance")]
        [DisplayName("标题前景色")]
        [Description("HeaderForeground")]
        [Editor(typeof(XProperty.Editors.XBrushEditor), typeof(XProperty.XPropertyValueEditor))]
        [XProperty(XPropertyCategory.Appearance, "HeaderForeground", "标题前景色")]
        public BrushEx XHeaderForeground
        {
            get
            {
                return (BrushEx)GetValue(XHeaderForegroundProperty);
            }
            set
            {
                SetValue(XHeaderForegroundProperty, value);
            }
        }

        [Category("Appearance")]
        [DisplayName("标题字体大小")]
        [Description("HeaderFontSize")]
        [Editor(typeof(XProperty.Editors.XDoubleEditor), typeof(XProperty.XPropertyValueEditor))]
        [XProperty(XPropertyCategory.Appearance, "HeaderFontSize", "标题字体大小")]
        public double XHeaderFontSize
        {
            get
            {
                return (double)GetValue(XHeaderFontSizeProperty);
            }
            set
            {
                SetValue(XHeaderFontSizeProperty, value);
            }
        }

        [Category("Appearance")]
        [DisplayName("标题字体")]
        [Description("HeaderFont")]
        [Editor(typeof(XProperty.Editors.XFontFamilyEditor), typeof(XProperty.XPropertyValueEditor))]
        [XProperty(XPropertyCategory.Appearance, "HeaderFont", "标题字体")]
        public FontFamily XHeaderFont
        {
            get
            {
                return (FontFamily)GetValue(XHeaderFontProperty);
            }
            set
            {
                SetValue(XHeaderFontProperty, value);
            }
        }
        #endregion

        private List<AreaPart> _subAreas;
        private List<ControlPart> _subControls;

        static AreaPart()
        {
            DefaultStyleKeyProperty.OverrideMetadata(typeof(AreaPart), new FrameworkPropertyMetadata(typeof(AreaPart)));

        }

        public AreaPart()
        {
            //XHeader = DateTime.Now.Ticks.ToString();
            XIsShowHeader = true;
            XHeaderWidth = 75;
            XHeaderHeight = 20;
            XHeaderPadding = new Thickness(0);
            XHeaderBackgroundImage = null;
            //this.Background = new System.Windows.Media.SolidColorBrush(System.Windows.Media.Colors.Red);

            _subAreas = new List<AreaPart>();
            _subControls = new List<ControlPart>();

            _partCollection = new PartCollection(this);
            _canvas = new Canvas();
            this.Content = _canvas;
        }

        ///// <summary>
        ///// 添加子区域
        ///// </summary>
        ///// <param name="areaPart"></param>
        //public void AddAreaPart(AreaPart areaPart)
        //{
        //    this._subAreas.Add(areaPart);
        //    this.AddPart(areaPart);
        //}

        //protected override void OnPartAdded(Part part)
        //{
        //    if (part is AreaPart)
        //    {
        //        this._subAreas.Add((AreaPart)part);
        //    }
        //}

        #region override

        protected override void OnInit(PartContext context)
        {
            base.OnInit(context);

            for (int index = 0; index < _partCollection.Count; index++)
            {
                _partCollection[index].Init(context);
            }
        }

        protected override void OnActive(PartContext context)
        {
            base.OnActive(context);

            for (int index = 0; index < _partCollection.Count; index++)
            {
                _partCollection[index].Active(context);
            }
        }

        protected override void OnDeactive(PartContext context)
        {
            for (int index = 0; index < _partCollection.Count; index++)
            {
                _partCollection[index].Deactive();
            }

            base.OnDeactive(context);
        }

        protected override void OnDestory()
        {
            for (int index = 0; index < _partCollection.Count; index++)
            {
                _partCollection[index].Destory();
            }

            // 移除引用
            for (int index = 0; index < _partCollection.Count; index++)
            {
                _canvas.Children.Remove(_partCollection[index]);

                _partCollection[index].ParentPart = null;
            }
            _partCollection.Clear();

            base.OnDestory();
        }

        protected override void OnFrozen()
        {
            for (int index = 0; index < this._partCollection.Count; index++)
            {
                this._partCollection[index].Frozen();
            }

            base.OnFrozen();
        }

        protected override void OnUnfreeze()
        {
            for (int index = 0; index < this._partCollection.Count; index++)
            {
                this._partCollection[index].Unfreeze();
            }

            base.OnUnfreeze();
        }

        protected override void OnReset()
        {
            base.OnReset();
            for (int index = 0; index < this._partCollection.Count; index++)
            {
                this._partCollection[index].Reset();
            }
        }
        protected override void OnRefresh()
        {
            base.OnRefresh();
            if (_partCollection == null)
            {
                return;
            }
            for (int index = 0; index < _partCollection.Count; index++)
            {
                _partCollection[index].Refresh();
            }
        }

        public override UI.Part GetPartAtPoint(Type type, Point pointInRoot, UIElement rootElement)
        {
            if (!XIsVisible)
            {
                return null;
            }
            UI.Part part = null;
            // Point absolutePoint = this.TranslatePoint(new Point(0, 0), rootElement);
            for (int index = 0; index < this._partCollection.Count; index++)
            {
                part = this._partCollection[index].GetPartAtPoint(type, pointInRoot, rootElement);
                if (part != null)
                {
                    return part;
                }
            }
            return base.GetPartAtPoint(type, pointInRoot, rootElement);
        }

        /// <summary>
        /// 测试是否x，y点是否命中了组件实例
        /// </summary>
        /// <param name="pointInScene">场景中的坐标点</param>
        /// <returns></returns>
        public override Data.XHitResult XHitTest(Point pointInScene)
        {
            if (!XIsVisible)
            {
                return new Data.XHitResult(false, null, default(Point));
            }

            Point pos = this.XGetAbsolutePos();
            Size size = this.XGetSize();

            if (pointInScene.X >= pos.X
                && pointInScene.Y >= pos.Y
                && pointInScene.X <= pos.X + size.Width
                && pointInScene.Y <= pos.Y + size.Height)
            {
                for (int index = this._partCollection.Count - 1; index >= 0; index--)
                {
                    Data.XHitResult hitResult = this._partCollection[index].XHitTest(pointInScene);
                    if (hitResult.IsSuccess)
                    {
                        return hitResult;
                    }
                }
                return new Data.XHitResult(true, this, new Point(pointInScene.X - pos.X, pointInScene.Y - pos.Y));
            }
            return new Data.XHitResult(false, null, default(Point));
        }

        /// <summary>
        /// 测试是否x，y点是否命中了组件实例
        /// </summary>
        /// <typeparam name="T">命中的组件必须属于这个类型</typeparam>
        /// <param name="pointInScene">场景中的坐标点</param>
        /// <returns></returns>
        public override Data.XHitResult XHitTest<T>(Point pointInScene)
        {
            if (!XIsVisible)
            {
                return new Data.XHitResult(false, null, default(Point));
            }

            Type type = typeof(T);
            Point pos = this.XGetAbsolutePos();
            Size size = this.XGetSize();

            if (pointInScene.X >= pos.X
                && pointInScene.Y >= pos.Y
                && pointInScene.X <= pos.X + size.Width
                && pointInScene.Y <= pos.Y + size.Height)
            {
                for (int index = this._partCollection.Count - 1; index >= 0; index--)
                {
                    Data.XHitResult hitResult = this._partCollection[index].XHitTest<T>(pointInScene);
                    if (hitResult.IsSuccess)
                    {
                        return hitResult;
                    }
                }
                if (this.GetType() == type || this.GetType().IsSubclassOf(type)
                    || type.IsAssignableFrom(this.GetType()))
                {
                    return new Data.XHitResult(true, this, new Point(pointInScene.X - pos.X, pointInScene.Y - pos.Y));
                }
            }
            return new Data.XHitResult(false, null, default(Point));
        }

        public Point XGetContentPos()
        {
            if (this.XIsShowHeader)
            {
                return new Point(this.Padding.Left,
                    this.Padding.Top + this.XHeaderHeight + this.XHeaderPadding.Top + this.XHeaderPadding.Bottom);
            }
            else
            {
                return new Point(this.Padding.Left, this.Padding.Top);
            }
        }

        #endregion

        #region Implement IPartContainer

        public List<Part> GetPartList()
        {
            return _partCollection.PartList;
        }

        /// <summary>
        /// 添加子组件
        /// </summary>
        /// <param name="part"></param>
        public void AddPart(Part part)
        {
            if (ValidateChildType(part.GetType()))
            {
                if (part is AreaPart)
                {
                    _partCollection.Insert(part, _subAreas.Count);
                    _canvas.Children.Insert(_subAreas.Count, part);
                    Canvas.SetZIndex(part, _subAreas.Count);
                    this._subAreas.Add((AreaPart)part);
                }
                else
                {
                    _partCollection.Add(part);
                    _canvas.Children.Add(part);
                    Canvas.SetZIndex(part, _canvas.Children.Count - 1);
                }

                Canvas.SetLeft(part, part.XLeft);
                Canvas.SetTop(part, part.XTop);

                part.ParentPart = this;
            }
        }

        public void XRemovePart(Part part)
        {
            if (part is AreaPart)
            {
                this._subAreas.Remove((AreaPart)part);
            }
            _partCollection.Remove(part);

            _canvas.Children.Remove(part);
        }

        /// <summary>
        /// 获取子组件
        /// </summary>
        /// <param name="partName"></param>
        /// <param name="isRecursive">是否递归</param>
        /// <returns></returns>
        public Part GetChildPart(string partName, bool isRecursive)
        {
            return _partCollection.GetChildPart(partName, isRecursive);
        }

        /// <summary>
        /// 根据类型来获取组件里包含的子组件
        /// </summary>
        /// <param name="isRecursive"></param>
        /// <returns></returns>
        public List<T> GetChildPartByType<T>(bool isRecursive)
        {
            return _partCollection.GetChildPartByType<T>(isRecursive);
        }

        public bool ValidateChildType(Type childType)
        {
            if (childType == typeof(AreaPart) || childType.IsSubclassOf(typeof(AreaPart)))
            {
                return true;
            }
            else if (childType.IsSubclassOf(typeof(ControlPart)))
            {
                return true;
            }
            return false;
        }

        /// <summary>
        /// 获取实际的可存放组件的容器控件
        /// </summary>
        /// <returns></returns>
        public UIElement GetContentContainer()
        {
            return this._canvas;
        }

        public void BringPartTo(Part part, int index)
        {
            XRemovePart(part);

            if (part is AreaPart)
            {
                this._subAreas.Insert(index, (AreaPart)part);
            }
            _partCollection.Insert(part, index);

            _canvas.Children.Insert(index, part);
            Canvas.SetZIndex(part, index);

            Canvas.SetLeft(part, part.XLeft);
            Canvas.SetTop(part, part.XTop);

            part.ParentPart = this;
        }
        public int GetPartIndex(Part part)
        {
            if (part != null)
            {
                if (_partCollection != null && _partCollection.Count > 0)
                {
                    if (part is AreaPart)
                    {
                        for (int index = _subAreas.Count - 1; index >= 0; index--)
                        {
                            if (_subAreas[index] == part)
                            {
                                return index;
                            }
                        }
                    }
                    else
                    {
                        for (int index = _partCollection.Count - 1; index >= 0; index--)
                        {
                            if (_partCollection[index] == part)
                            {
                                return index;
                            }
                        }
                    }
                }
            }
            return -1;
        }

        public int GetSendToBackIndex(Part part)
        {
            if(part is AreaPart)
            {
                return 0;
            }
            else
            {
                return _subAreas.Count;
            }
        }

        public int GetBringToFrontIndex(Part part)
        {
            if (part is AreaPart)
            {
                return _subAreas.Count - 1;
            }
            else
            {
                return _partCollection.Count - 1;
            }
        }
        public int GetForwardIndex(Part part)
        {
            int index = GetPartIndex(part);
            if (part is AreaPart)
            {
                return index < _subAreas.Count - 1 ? index + 1 : index;
            }
            else
            {
                return index < _partCollection.Count - 1 ? index + 1 : index;
            }
        }

        public int GetBackwardIndex(Part part)
        {
            int index = GetPartIndex(part);
            if (part is AreaPart)
            {
                return index > 0 ? index - 1 : index;
            }
            else
            {
                return index > _subAreas.Count ? index - 1 : index;
            }
        }

        public Tuple<Point?, Point?, byte> GetAlignLinePoint(Part part, double left = -1, double top = -1, double right = -1, double bottom = -1)
        {
            //00表示left和top，01表示right和top，10表示left和bottom，11表示right和bottom
            byte bDirection = 0;
            Point? leftOrRight = null;
            Point? topOrBottom = null;
            double minOffsetX = double.MaxValue;
            double minOffsetY = double.MaxValue;
            for (int index = 0; index < _partCollection.Count; index++)
            {
                Part destPart = _partCollection[index];
                if (destPart != part)
                {
                    if(left >= 0)
                    {
                        if (Math.Abs(destPart.XLeft - left) < 2 && Math.Abs(destPart.XLeft - left) < minOffsetX)
                        {
                            minOffsetX = Math.Abs(destPart.XLeft - left);
                            leftOrRight = new Point(destPart.XLeft, destPart.XTop + destPart.XHeight / 2);
                            bDirection &= 2;
                        }
                        else if(Math.Abs(destPart.XLeft + destPart.XWidth - left) < 2 && Math.Abs(destPart.XLeft + destPart.XWidth - left) < minOffsetX)
                        {
                            minOffsetX = Math.Abs(destPart.XLeft + destPart.XWidth - left);
                            leftOrRight = new Point(destPart.XLeft + destPart.XWidth, destPart.XTop + destPart.XHeight / 2);
                            bDirection &= 2;
                        }
                    }
                    if(top >= 0)
                    {
                        if (Math.Abs(destPart.XTop - top) < 2 && Math.Abs(destPart.XTop - top) < minOffsetY)
                        {
                            minOffsetY = Math.Abs(destPart.XTop - top);
                            topOrBottom = new Point(destPart.XLeft + destPart.XWidth / 2, destPart.XTop);
                            bDirection &= 1;
                        }
                        else if (Math.Abs(destPart.XTop + destPart.XHeight - top) < 2 && Math.Abs(destPart.XTop + destPart.XHeight - top) < minOffsetY)
                        {
                            minOffsetY = Math.Abs(destPart.XTop + destPart.XHeight - top);
                            topOrBottom = new Point(destPart.XLeft + destPart.XWidth / 2, destPart.XTop + destPart.XHeight);
                            bDirection &= 1;
                        }
                    }
                    if (right >= 0)
                    {
                        if (Math.Abs(destPart.XLeft - right) < 2 && Math.Abs(destPart.XLeft - right) < minOffsetX)
                        {
                            minOffsetX = Math.Abs(destPart.XLeft - right);
                            leftOrRight = new Point(destPart.XLeft, destPart.XTop + destPart.XHeight / 2);
                            bDirection |= 1;
                        }
                        else if (Math.Abs(destPart.XLeft + destPart.XWidth - right) < 2 && Math.Abs(destPart.XLeft + destPart.XWidth - right) < minOffsetX)
                        {
                            minOffsetX = Math.Abs(destPart.XLeft + destPart.XWidth - right);
                            leftOrRight = new Point(destPart.XLeft + destPart.XWidth, destPart.XTop + destPart.XHeight / 2);
                            bDirection |= 1;
                        }
                    }
                    if (bottom >= 0)
                    {
                        if (Math.Abs(destPart.XTop - bottom) < 2 && Math.Abs(destPart.XTop - bottom) < minOffsetY)
                        {
                            minOffsetY = Math.Abs(destPart.XTop - bottom);
                            topOrBottom = new Point(destPart.XLeft + destPart.XWidth / 2, destPart.XTop);
                            bDirection |= 2;
                        }
                        else if (Math.Abs(destPart.XTop + destPart.XHeight - bottom) < 2 && Math.Abs(destPart.XTop + destPart.XHeight - bottom) < minOffsetY)
                        {
                            minOffsetY = Math.Abs(destPart.XTop + destPart.XHeight - bottom);
                            topOrBottom = new Point(destPart.XLeft + destPart.XWidth / 2, destPart.XTop + destPart.XHeight);
                            bDirection |= 2;
                        }
                    }
                }
            }
            return new Tuple<Point?, Point?, byte>(leftOrRight, topOrBottom, bDirection);
        }

        #endregion

        public override void RaiseScriptNetEvent(string eventName, string dataString)
        {
            base.RaiseScriptNetEvent(eventName, dataString);
            for (int index = 0; index < _partCollection.Count; index++)
            {
                _partCollection[index].RaiseScriptNetEvent(eventName, dataString);
            }
        }
    }
}
